package com.ohuang.util_h.systemServiceHook;

import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;


import com.ohuang.util_h.RefInvoke;

import java.io.FileDescriptor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 *  用于hook systemService
 */
public class BinderInvocationStub implements IBinder {
    private IBinder mBaseBinder;
    private String class_IInterface;
    private InvocationHandler invocationHandler;

    private Object base;

    /**
     *
     * @param baseBinder  通过getSystemService() 获取
     * @param class_IInterface  IInterface对应类名
     * @param invocationHandler  提供的hook方法
     */
    public BinderInvocationStub(IBinder baseBinder, String class_IInterface, InvocationHandler invocationHandler) {
        this.mBaseBinder = baseBinder;
        this.class_IInterface = class_IInterface;
        this.invocationHandler = invocationHandler;
        init();
    }

    private void init() {
        String className = class_IInterface + "$Stub";
        base = RefInvoke.invokeStaticMethod(className, "asInterface", new Class[]{IBinder.class}, new Object[]{mBaseBinder});
    }


    
    @Override
    public String getInterfaceDescriptor() throws RemoteException {
        return mBaseBinder.getInterfaceDescriptor();
    }

    @Override
    public boolean pingBinder() {
        return mBaseBinder.pingBinder();
    }

    @Override
    public boolean isBinderAlive() {
        return mBaseBinder.isBinderAlive();
    }

    
    @Override
    public IInterface queryLocalInterface( String descriptor) {
        try {
            return (IInterface) getProxyInvocation();
        } catch (ClassNotFoundException e) {

        }
        return mBaseBinder.queryLocalInterface(descriptor);
    }

    private Object getProxyInvocation() throws ClassNotFoundException {
        return Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Class.forName(class_IInterface)}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return invocationHandler.invoke(base, method, args);
            }
        });
    }

    @Override
    public void dump( FileDescriptor fd,  String[] args) throws RemoteException {
        mBaseBinder.dump(fd, args);
    }

    @Override
    public void dumpAsync( FileDescriptor fd,  String[] args) throws RemoteException {
        mBaseBinder.dumpAsync(fd, args);
    }

    @Override
    public boolean transact(int code,  Parcel data,  Parcel reply, int flags) throws RemoteException {
        return mBaseBinder.transact(code, data, reply, flags);
    }

    @Override
    public void linkToDeath( DeathRecipient recipient, int flags) throws RemoteException {
        mBaseBinder.linkToDeath(recipient, flags);
    }

    @Override
    public boolean unlinkToDeath( DeathRecipient recipient, int flags) {
        return mBaseBinder.unlinkToDeath(recipient, flags);
    }


    public static IBinder getSystemService(String name) {
        Object getService = RefInvoke.invokeStaticMethod("android.os.ServiceManager", "getService", new Class[]{String.class}, new Object[]{name});
        if (getService != null) {
            return (IBinder) getService;
        }
        Map<String, IBinder> sCache = (Map<String, IBinder>) RefInvoke.getStaticFieldOjbect("android.os.ServiceManager", "sCache");
        return sCache.get(name);
    }

    public static void replaceSystemService(String name, IBinder iBinder) {
        Map<String, IBinder> sCache = (Map<String, IBinder>) RefInvoke.getStaticFieldOjbect("android.os.ServiceManager", "sCache");
        sCache.put(name, iBinder);
    }
}
